﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Dexter.Dependency;
using Dexter.Domain.Model;
using Dexter.Logger.Contracts;
using Dexter.Scheduler;
using Dexter.Services;

namespace Dexter.PluginManager {
	public class PluginManager : IPluginManager {
		readonly IDexterContainer dexterContainer;
		readonly IPluginService pluginService;
		readonly ILogger logger;

		public PluginManager(IDexterContainer dexterContainer, IPluginService pluginService, ILogger logger) {
			this.dexterContainer = dexterContainer;
			this.pluginService = pluginService;
			this.logger = logger;
		}

		#region IPluginManager Members

		public void LoadPlugins() {
			LoadPlugins(TimeSpan.Zero);
		}

		public void LoadPlugins(TimeSpan deplay) {
			string pluginsRoot = AppDomain.CurrentDomain.BaseDirectory + "App_Data\\Plugins\\";

			if (!Directory.Exists(pluginsRoot))
				Directory.CreateDirectory(pluginsRoot);

			string[] plugins = Directory.GetDirectories(pluginsRoot);

			foreach (string plugin in plugins) {
				FileInfo[] assemblies = new DirectoryInfo(plugin).GetFiles("*.dll");

				foreach (FileInfo fileInfo in assemblies) {
					Assembly pluginAssemblies = Assembly.Load(File.ReadAllBytes(fileInfo.FullName));

					List<Type> pi = pluginAssemblies.GetTypes().Where(t => !t.IsInterface && !t.IsAbstract && typeof (IPluginInfo).IsAssignableFrom(t)).ToList();

					foreach (var type in pi) {
						var instance = (IPluginInfo)Activator.CreateInstance(type);
						dexterContainer.Register(GetPluginInfoKey(instance.Identifier), typeof(IPluginInfo), type, LifeCycle.Singleton);
					}
				}
			}

			SaveNewPlugns();
		}

		void SaveNewPlugns() {
			IList<Plugin> storedPlugin = pluginService.GetAllPlugins();
			IList<Plugin> toBestoredPlugin = new List<Plugin>();

			IPluginInfo[] loadedPlugins = dexterContainer.ResolveAll<IPluginInfo>();

			foreach (var loadedPlugin in loadedPlugins)
			{
				var sp = storedPlugin.SingleOrDefault(x => x.Identifier == loadedPlugin.Identifier);

				if (sp == null)
				{
					var p = Plugin.CreateNewPlugin(loadedPlugin.Identifier, loadedPlugin.Name, loadedPlugin.Version, loadedPlugin.Identifier.ToString());
					p.Description = loadedPlugin.Description;
					p.Website = loadedPlugin.WebSite;
					p.Author = loadedPlugin.Author;

					toBestoredPlugin.Add(p);
				}
			}

			foreach (Plugin plugin in toBestoredPlugin)
			{
				pluginService.SaveOrUpdate(plugin);
			}
		}

		public void EnablePlugins() {
			var pluginToEnable = pluginService.GetAllPlugins().ToList();
			var loadedPlugins = dexterContainer.ResolveAll<IPluginInfo>();

			foreach (Plugin plugin in pluginToEnable) {
				var pi = loadedPlugins.SingleOrDefault(x => x.Identifier == plugin.Identifier);

				if(pi == null)
				{
					logger.ErrorFormat("unable to load the specified plugin: {0}", plugin.Identifier);
					continue;
				}

				var key = GetPluginConfigurationKey(pi.Identifier);

				dexterContainer.Register(key, typeof(IPluginConfiguration), pi.IPluginConfiguration, LifeCycle.Singleton);
				dexterContainer.Register(pi.Identifier.ToString(), typeof(IPlugin), pi.ImplementedBy, LifeCycle.Singleton);
				
				plugin.Enable();

				if (!plugin.IsInstalled) {
					dexterContainer.Resolve<IPlugin>(pi.Identifier.ToString()).Setup();
					plugin.SetInstalled();
				}

				pluginService.SaveOrUpdate(plugin);
			}

			var loadedRenderPlugins = dexterContainer.ResolveAll<IPlugin>();

			loadedRenderPlugins.ForEach(x => x.RegisterServices(dexterContainer));
		}

		static string GetPluginConfigurationKey(Guid identifier)
		{
			return string.Format("plugin_configuration_{0}", identifier);
		}

		static string GetPluginInfoKey(Guid identifier)
		{
			return string.Format("plugin_info_{0}", identifier);
		}

		public IEnumerable<IPlugin> GetRenderPlugins() {
			return dexterContainer.ResolveAll<IPlugin>();
		}

		public IPluginInfo GetPluginInfo(Guid identifier) {
			return dexterContainer.Resolve<IPluginInfo>(GetPluginInfoKey(identifier));
		}

		public IPluginConfiguration GetPluginConfiguration(Guid identifier) {
			return dexterContainer.Resolve<IPluginConfiguration>(GetPluginConfigurationKey(identifier));
		}

		#endregion
	}
}